﻿using System;
using System.ComponentModel;
using System.Reflection.Emit;
using System.Web.UI.WebControls;
using System.Web.UI;

namespace MyLib.Web.UI {
	/// <summary>
	/// 
	/// </summary>
	public sealed class MyObjectDataSourceGettingViewEventArgs : EventArgs {
		/// <summary>
		/// 
		/// </summary>
		public System.Data.DataView View {
			get;
			set;
		}

		/// <summary>
		/// 
		/// </summary>
		public MyObjectDataSourceGettingViewEventArgs() {
			View = null;
		}
	}

	/// <summary>
	/// 
	/// </summary>
	[Description("MyObjectDataSource_Description")]
	[DisplayName("MyObjectDataSource_DisplayName")]
	[DefaultEvent("Selecting")]
	[DefaultProperty("TypeName")]
	[ParseChildren(true)]
	[PersistChildren(false)]
	[ToolboxData("<{0}:MyObjectDataSource runat=\"server\"></{0}:MyObjectDataSource>")]
	public class MyObjectDataSource : ObjectDataSource {
		/// <summary>
		/// 
		/// </summary>
		public event EventHandler<MyObjectDataSourceGettingViewEventArgs> GettingView;

		/// <summary>
		/// 
		/// </summary>
		private class DataViewSourceView : DataSourceView {
			private System.Data.DataView m_view;

			/// <summary>
			/// 
			/// </summary>
			/// <param name="arguments"></param>
			/// <returns></returns>
			protected override System.Collections.IEnumerable ExecuteSelect(DataSourceSelectArguments arguments) {
				return m_view;
			}

			/// <summary>
			/// 
			/// </summary>
			/// <param name="owner"></param>
			/// <param name="viewName"></param>
			/// <param name="view"></param>
			public DataViewSourceView(IDataSource owner, string viewName, System.Data.DataView view)
				: base(owner, viewName) {
				m_view = view;
			}
		} // end of DataViewSourceView.

		/// <summary>
		/// 
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnGettingView(MyObjectDataSourceGettingViewEventArgs args) {
			if (GettingView != null) {
				GettingView(this, args);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public MyObjectDataSource() {
			GetObjectDataSourceViewPrivateSetter()(this, new MyObjectDataSourceView(this, "DefaultView", Context));

			EnablePaging = true;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="typeName"></param>
		/// <param name="selectMethod"></param>
		public MyObjectDataSource(string typeName, string selectMethod)
			: base(typeName, selectMethod) {
			var view = new MyObjectDataSourceView(this, "DefaultView", Context);
			view.TypeName = typeName;
			view.SelectMethod = selectMethod;
			GetObjectDataSourceViewPrivateSetter()(this, view);

			EnablePaging = true;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="viewName"></param>
		/// <returns></returns>
		protected override DataSourceView GetView(string viewName) {
			var args = new MyObjectDataSourceGettingViewEventArgs();

			OnGettingView(args);

			if (args.View != null) {
				return new DataViewSourceView(this, viewName, args.View);
			} else {
				return base.GetView(viewName);
			}
		}

		private static Action<ObjectDataSource, ObjectDataSourceView> m_objectDataSourceViewPrivateSetter = null;

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		private static Action<ObjectDataSource, ObjectDataSourceView> GetObjectDataSourceViewPrivateSetter() {
			if (m_objectDataSourceViewPrivateSetter == null) {
				var field = typeof(ObjectDataSource).GetField("_view", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

				DynamicMethod dm = new DynamicMethod("_SetView_", typeof(void),
												 new Type[] { typeof(ObjectDataSource), typeof(ObjectDataSourceView) },
												 typeof(ObjectDataSource), true);
				ILGenerator generator = dm.GetILGenerator();

				generator.Emit(OpCodes.Ldarg_0);
				generator.Emit(OpCodes.Ldarg_1);
				generator.Emit(OpCodes.Stfld, field);
				generator.Emit(OpCodes.Ret);

				m_objectDataSourceViewPrivateSetter = (Action<ObjectDataSource, ObjectDataSourceView>)
					dm.CreateDelegate(typeof(Action<ObjectDataSource, ObjectDataSourceView>));
			}

			return m_objectDataSourceViewPrivateSetter;
		}
	} // end of MyObjectDataSource.
}
